package com.spelet.screens;

import java.io.IOException;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.spelet.Main;
import com.spelet.Renderer;
import com.spelet.StaticVariables;
import com.spelet.VariableCollection;
import com.spelet.simulation.Block;
import com.spelet.simulation.ChooseBlockBrush;
import com.spelet.simulation.ChooseTerrainBrush;
import com.spelet.simulation.LoadMapFile;
import com.spelet.simulation.Map;
import com.spelet.simulation.ResizeMap;
import com.spelet.simulation.SaveMapFile;
import com.spelet.simulation.TerrainBlock;
import com.spelet.simulation.ViewBar;

public class MapEditor extends Screen {

	public Main main;
	public float mapZoom = 1f;
	public int numberOfFingers = 0;
	public int fingerOnePointer;
	public int fingerTwoPointer;
	public float lastDistance = 0;
	public Vector3 fingerOne = new Vector3();
	public Vector3 fingerTwo = new Vector3();
	public Map map;
	public float minZoomValue;
	public float maxZoomValue;
	public com.spelet.simulation.Character bob = new com.spelet.simulation.Character();
	public int placingBlock = VariableCollection.nothing, placingTerrain = VariableCollection.nothing;
	public int bobX = 0, bobY = 0, xint, yint;
	public boolean chooseTerrainBrush, chooseBlockBrush, savingFile, loadingFile, chooseNewSize;
	public String newXsize = "", newYsize = "";
	public Array<Character> nameText;
	public ChooseTerrainBrush ctb;
	public ChooseBlockBrush cbb;
	public ResizeMap rm;
	public LoadMapFile lmf;
	public SaveMapFile smf;
	public ViewBar viewbar;
	public boolean pressedCTB;
	public boolean pressedCBB;
	public boolean pressedLMF;
	public int oldX, oldY;
	private Rectangle terrainRect = new Rectangle(StaticVariables.edgeDistance,
			StaticVariables.edgeDistance,StaticVariables.buttonSize,StaticVariables.buttonSize);
	private Rectangle objectsRect = new Rectangle(StaticVariables.edgeDistance+StaticVariables.buttonOffset*1f,
			StaticVariables.edgeDistance,StaticVariables.buttonSize,StaticVariables.buttonSize);
	private Rectangle testRect = new Rectangle(StaticVariables.edgeDistance+StaticVariables.buttonOffset*2f,
			StaticVariables.edgeDistance,StaticVariables.buttonSize,StaticVariables.buttonSize);
	private Rectangle resizeRect = new Rectangle(StaticVariables.edgeDistance+StaticVariables.buttonOffset*3f,
			StaticVariables.edgeDistance,StaticVariables.buttonSize,StaticVariables.buttonSize);
	private Rectangle saveRect = new Rectangle(StaticVariables.edgeDistance+StaticVariables.buttonOffset*4f,
			StaticVariables.edgeDistance,StaticVariables.buttonSize,StaticVariables.buttonSize);
	private Rectangle loadRect = new Rectangle(StaticVariables.edgeDistance+StaticVariables.buttonOffset*5f,
			StaticVariables.edgeDistance,StaticVariables.buttonSize,StaticVariables.buttonSize);
	private Rectangle exitRect = new Rectangle(StaticVariables.edgeDistance+StaticVariables.buttonOffset*6f,
			StaticVariables.edgeDistance,StaticVariables.buttonSize,StaticVariables.buttonSize);

	// Fixa
	boolean draggingRow;
	boolean draggingCol;
	public int draggingRownr;
	public int draggingColnr;
	public int draggedToRownr = Integer.MAX_VALUE;
	public int draggedToColnr = Integer.MAX_VALUE;
	Vector3 vec = new Vector3(0,0,0);

	public boolean renderingBlocks;
	public boolean renderingTerrain;
	public boolean wallsAround;
	public MapEditor(Main m) {
		renderingBlocks = true;
		renderingTerrain = true;
		viewbar = new ViewBar();
		ctb = new ChooseTerrainBrush();
		cbb = new ChooseBlockBrush();
		lmf = new LoadMapFile();
		smf = new SaveMapFile();
		rm = new ResizeMap();
		nameText = new Array<Character>();
		this.main = m;
		this.map = new Map();
		setMapSize(10,10);
	}

	@Override
	public boolean keyDown(int arg0) {
		if (Input.Keys.BACK == arg0) {
			main.setScreen(Main.SCREEN_MAINMENU);
		}
		return false;
	}

	@Override
	public boolean keyTyped(char finalChar) {
		if (savingFile) {
			if (finalChar == 8){ // backspace
				if (nameText.size>0) 
					nameText.removeIndex(nameText.size-1);
				StringBuilder fName = new StringBuilder();
				for (int i = 0; i < nameText.size; i++) {
					fName.append(nameText.get(i));
				}
				smf.setName(fName.toString());
			} else if (finalChar == 13 || finalChar == 10) { // enter
				System.out.println(map.toString(bob));
				StringBuilder fName = new StringBuilder();
				for (int i = 0; i < nameText.size; i++) {
					fName.append(nameText.get(i));
				}
				if (fName.toString().length() > 0) {
					FileHandle f = Gdx.files.external("GreenHatMan/"+fName.toString()+".GHM");
					if (f.exists()) {
						smf.addErrorMessage("There is already a file with the name " + fName.toString() + ".");
					} else {
						try {
							Gdx.app.log("key", "tried" + f.file().getAbsolutePath());
							f.file().createNewFile();
							f.writeString(map.toString(bob), false);
							savingFile = false;
							Gdx.input.setOnscreenKeyboardVisible(false);
							Gdx.app.log("key", "all good");
						} catch (IOException e) {
							// TODO Auto-generated catch block
							Gdx.app.log("key", "failed");
							e.printStackTrace();
						}
					}
				} else {
					smf.addErrorMessage("You must name your file!");
				}
			} else if ("abcdefghijklmnopqrstuvxyz".contains(Character.toString(finalChar).toLowerCase())){
				nameText.add(finalChar);
				StringBuilder fName = new StringBuilder();
				for (int i = 0; i < nameText.size; i++) {
					fName.append(nameText.get(i));
				}
				smf.setName(fName.toString());
			} else if (!"abcdefghijklmnopqrstuvxyz".contains(Character.toString(finalChar).toLowerCase())){
				smf.addErrorMessage("\"" + Character.toString(finalChar) + "\" is an invalid character.");
			}
		}
		return false;
	}

	@Override
	public boolean keyUp(int arg0) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean scrolled(int arg0) {
		mapZoom += arg0>0?0.1f:-0.1f;
		if(mapZoom < minZoomValue)
			mapZoom = minZoomValue;
		else if(mapZoom > maxZoomValue)
			mapZoom = maxZoomValue;
		Renderer.getInstance().mapCam.zoom = mapZoom;
		System.out.println("New zoom: "+mapZoom);
		return false;
	}

	@Override
	public boolean touchDown(int x, int y, int pointer, int button) {
		pressedCTB = false;
		pressedCBB = false;
		vec.set(x,y,0);
		Renderer.getInstance().mapCam.unproject(vec);
		if (viewbar.hit(x,Gdx.graphics.getHeight()-y)) {
			viewbar.pressed(this,x,Gdx.graphics.getHeight()-y);
		} else if (!hitUI(x,y)) {
			if (vec.x > -128 && vec.x < -64 && vec.y > -64 && vec.y < 0) {
				if (vec.y > -64 && vec.y < 0) {
					if (map.rows <= Map.MIN_ROWS) {
						System.out.println("Cannot have less than "+Map.MIN_ROWS+" rows.");
					} else {
						if(bobY > 0)
							bobY--;
						setMapSize(map.cols,map.rows-1);
					}
				}
			} else if (vec.x > -64 && vec.x < 0) {
				for (int y2 = 0; y2 < map.terrainBlocks[0].length;y2++) {
					if (vec.y > y2*64 && vec.y < y2*64+64) {
						System.out.println("Clicked row: " + y2);
						draggingRow = true;
						draggingRownr = y2;
						draggedToRownr = y2;
					}
				}
				if (vec.y > -64 && vec.y < 0) {
					if (map.rows >= Map.MAX_ROWS) {
						System.out.println("Cannot have more than "+Map.MAX_ROWS+" rows.");
					} else {
						bobY++;
						setMapSize(map.cols,map.rows+1);
					}
				}
			} else if (vec.y > map.terrainBlocks[0].length*64 && vec.y < map.terrainBlocks[0].length*64+64) {
				for (int x2 = 0; x2 < map.terrainBlocks.length; x2++) {
					if (vec.x > x2*64 && vec.x < x2*64+64) {
						System.out.println("Clicked column: " + x2);
						draggingCol = true;
						draggingColnr = x2;
						draggedToColnr = x2;
					}
				}
				if (vec.x > map.terrainBlocks.length*64 && vec.x < map.terrainBlocks.length*64+64) {
					if(map.cols >= Map.MAX_COLS)
						System.out.println("Cannot have more than "+Map.MAX_COLS+" columns.");
					else
						setMapSize(map.cols+1, map.rows);
				}
			} else if (vec.y > map.terrainBlocks[0].length*64+64 && vec.y < map.terrainBlocks[0].length*64+128) {
				if (vec.x > map.terrainBlocks.length*64 && vec.x < map.terrainBlocks.length*64+64) {
					if(map.cols <= Map.MIN_COLS)
						System.out.println("Cannot have less than "+Map.MIN_COLS+" columns.");
					else
						setMapSize(map.cols-1, map.rows);
				}
			}
			if (chooseTerrainBrush && ctb.yes.getBoundingRectangle().contains(x,Gdx.graphics.getHeight()-y)) {
				placingTerrain = ctb.valToVarVal[ctb.selectedType];
				placingBlock = VariableCollection.nothing;
				viewbar.setBrush(placingTerrain);
				chooseTerrainBrush = false;
				return false;
			} else if (chooseTerrainBrush && ctb.no.getBoundingRectangle().contains(x,Gdx.graphics.getHeight()-y)) {
				chooseTerrainBrush = false;
				return false;
			} else if (chooseTerrainBrush && ctb.hit(x,Gdx.graphics.getHeight()-y)) {
				ctb.pressed(x,Gdx.graphics.getHeight()-y);
				pressedCTB = true;
				oldX = x;
				return false;
			} else if (chooseBlockBrush && cbb.yes.getBoundingRectangle().contains(x,Gdx.graphics.getHeight()-y)) {
				placingBlock = cbb.valToVarVal[cbb.selectedType];
				placingTerrain = VariableCollection.nothing;
				viewbar.setBrush(placingBlock);
				chooseBlockBrush = false;
				return false;
			} else if (chooseBlockBrush && cbb.no.getBoundingRectangle().contains(x,Gdx.graphics.getHeight()-y)) {
				chooseBlockBrush = false;
				return false;
			} else if (chooseBlockBrush && cbb.hit(x, Gdx.graphics.getHeight()-y)) {
				cbb.pressed(x,Gdx.graphics.getHeight()-y);
				pressedCBB = true;
				oldX = x;
				return false;
			} else if (loadingFile && lmf.open.getBoundingRectangle().contains(x,Gdx.graphics.getHeight()-y) && lmf.currentFile != null) {
				if (lmf.clickedOpen()) {
					map.MElvl(bob,lmf.getCurrentFileData());
					bobX = bob.tileX;
					bobY = bob.tileY;
					loadingFile = false;
				}
			} else if (loadingFile && lmf.hit(x,Gdx.graphics.getHeight()-y)) {
				pressedLMF = true;
				oldY = y;
				lmf.pressed(x,Gdx.graphics.getHeight()-y);
			} else if (chooseNewSize && rm.set.getBoundingRectangle().contains(x,Gdx.graphics.getHeight()-y)) {
				setMapSize(rm.cols, rm.rows);
				chooseNewSize = false;
			} else if (chooseNewSize && rm.hit(x,Gdx.graphics.getHeight()-y)) {
				rm.pressed(x,Gdx.graphics.getHeight()-y);
			}
		}
		numberOfFingers++;

		if(numberOfFingers == 1){
			fingerOnePointer = pointer;
			fingerOne.set(x, y, 0);
		}else if(numberOfFingers == 2){
			fingerTwoPointer = pointer;
			fingerTwo.set(x, y, 0);

			float distance = fingerOne.dst(fingerTwo);
			lastDistance = distance;
		}


		if(hitUI(x,y)) {

			y = Gdx.graphics.getHeight()-y;

			chooseTerrainBrush = false;
			chooseBlockBrush = false;
			savingFile = false;
			Gdx.input.setOnscreenKeyboardVisible(false);
			loadingFile = false;
			chooseNewSize = false;
			newXsize = "";
			newYsize = "";
			nameText.clear();

			if(terrainRect.contains(x,y)) {
				chooseTerrainBrush = true;
			} else if(objectsRect.contains(x,y)) {
				chooseBlockBrush = true;
			} else if (testRect.contains(x,y)) {
				((Game) main.getState(Main.SCREEN_GAME)).testLevel(map.toString(bob));
				main.setScreen(Main.SCREEN_GAME);
			} else if(resizeRect.contains(x,y)) {
				chooseNewSize = true;
			} else if(saveRect.contains(x,y)) {
				savingFile = true;
				Gdx.input.setOnscreenKeyboardVisible(true);
			} else if(loadRect.contains(x,y)) {
				loadingFile = true;
			} else if(exitRect.contains(x,y)) {
				main.setScreen(Main.SCREEN_MAINMENU);
			}
		} else {
			float translatedX = vec.x;
			float translatedY = vec.y;
			if(translatedX < 0 || translatedX > map.cols*64f || translatedY < 0 || translatedY > map.rows*64f) {
			} else{
				int gridX = (int)translatedX/64;
				int gridY = (int)translatedY/64;
				if(placingTerrain != VariableCollection.nothing) {
					if(placingTerrain == VariableCollection.EditorEraser)
						map.terrainBlocks[gridX][gridY] = new TerrainBlock(VariableCollection.grass,gridX,gridY);
					else
						map.terrainBlocks[gridX][gridY] = new TerrainBlock(placingTerrain,gridX,gridY);
				} else {
					int currboby = bobY;

					if(gridX == bobX && gridY == bobY) {
					} else {
						if(placingBlock == VariableCollection.CharDummy) {
							System.out.println("GreenHatMan adjusts to his new surroundings.");
							bobX = gridX; bobY = gridY;
							map.blocks[bobX][bobY] = new Block(VariableCollection.nothing,bobX,bobY);
							bob.setPosition(bobX, bobY);
						} else if(placingBlock != VariableCollection.nothing) {
							int prevType = map.blocks[gridX][gridY].type;
							if(placingBlock == VariableCollection.EditorEraser) {
								map.blocks[gridX][gridY] = new Block(VariableCollection.nothing,gridX,gridY);
							} else {
								map.blocks[gridX][gridY] = new Block(placingBlock,gridX,gridY);
							}

							if (placingBlock == VariableCollection.wall || prevType == VariableCollection.wall) {
								for (int x2 = 0; x2 < map.blocks.length; x2++) {
									for (int y2 = 0; y2 < map.blocks[0].length; y2++) {
										map.blocks[x2][y2].setSprite(x2,y2,map.blocks);
									}
								}
							}
						}
					}

					map.renderAtRows.get(gridY).clear();
					for(int i=0;i<map.cols;i++) {
						map.renderAtRows.get(gridY).add(map.blocks[i][gridY]);
					}

					map.renderAtRows.get(currboby).clear();
					for(int i=0;i<map.cols;i++) {
						map.renderAtRows.get(currboby).add(map.blocks[i][currboby]);
					}
				}
			}
		}

		return false;
	}

	public boolean hitUI(int x, int y) {
		return y > (Gdx.graphics.getHeight() - StaticVariables.percentWidth*12f) || viewbar.hit(x, Gdx.graphics.getHeight()-y);
	}

	public boolean touchDragged(int x, int y, int pointer) {
		vec.set(x,y,0);
		Renderer.getInstance().mapCam.unproject(vec);
		if (chooseTerrainBrush && pressedCTB) {
			ctb.changeDelta(oldX-x);
			oldX = x;
			return false;
		} else if (chooseBlockBrush && pressedCBB) {
			cbb.changeDelta(oldX-x);
			oldX = x;
			return false;
		} else if (loadingFile && pressedLMF) {
			lmf.changeDelta(oldY-y);
			oldY = y;
		} else if (draggingCol) {
			for (int x2 = 0; x2 < map.terrainBlocks.length; x2++) {
				if (vec.x > x2*64 && vec.x < x2*64+64 && vec.y < map.terrainBlocks.length*64+64) {
					if(draggingColnr != x2)
					{
						System.out.println(draggingColnr + " dragged to " + x2);
						//map.moveColTo(draggedToColnr, draggingColnr);
						draggedToColnr = x2;
						if(bobX == draggedToColnr) {
							bobX = draggingColnr;
							bob.setPosition(bobX,bobY);
						}
						else if(bobX == draggingColnr){
							bobX = draggedToColnr;
							bob.setPosition(bobX,bobY);
						}
						map.swapCols(draggingColnr, draggedToColnr);
						draggingColnr = x2;
					}
				}
			}
		} else if (draggingRow) {
			for (int y2 = 0; y2 < map.terrainBlocks[0].length;y2++) {
				if (vec.y > y2*64 && vec.y < y2*64+64 && vec.x > -64) {
					if(draggingRownr != y2)
					{
						System.out.println(draggingRownr + " dragged to " + y2);
						draggedToRownr = y2;
						if(bobY == draggedToRownr)
						{
							bobY = draggingRownr;
							bob.setPosition(bobX,bobY);
						}
						else if(bobY == draggingRownr)
						{
							bobY = draggedToRownr;
							bob.setPosition(bobX,bobY);
						}
						map.swapRows(draggingRownr, draggedToRownr);
						draggingRownr = y2;
					}
				}
			}
		} else {
			// for pinch-to-zoom
			if (pointer == fingerOnePointer) {
				float deltaX = fingerOne.x - x;
				float deltaY = y - fingerOne.y;
				fingerOne.set(x, y, 0);
				if(numberOfFingers == 1)
					Renderer.getInstance().mapCam.translate(deltaX*mapZoom, deltaY*mapZoom,0);
			}
			if (pointer == fingerTwoPointer) {
				fingerTwo.set(x, y, 0);
			}

			if(numberOfFingers == 2) {
				float distance = fingerOne.dst(fingerTwo);
				float factor = distance / lastDistance;

				if (lastDistance > distance) {
					mapZoom += factor;
				} else if (lastDistance < distance) {
					mapZoom -= factor;
				}

				if(mapZoom < minZoomValue)
					mapZoom = minZoomValue;
				else if(mapZoom > maxZoomValue)
					mapZoom = maxZoomValue;
				Renderer.getInstance().mapCam.zoom = mapZoom;
				lastDistance = distance;
			}
		}
		return false;
	}
	public void wallEdges(boolean wall, int rows, int cols) {
		if (rows > map.rows)
			rows = map.rows;
		if (cols > map.cols)
			cols = map.cols;
		wallsAround = wall;
		int v = VariableCollection.nothing;
		if (wall)
			v = VariableCollection.wall;
		for (int x = 0; x < cols; x++) {
			map.blocks[x][0].type = v;
			map.terrainBlocks[x][0].type = VariableCollection.grass;
			map.blocks[x][rows-1].type = v;
			map.terrainBlocks[x][rows-1].type = VariableCollection.grass;
		}
		for (int y = 1; y < rows-1; y++) {
			map.blocks[0][y].type = v;
			map.terrainBlocks[0][y].type = VariableCollection.grass;
			map.blocks[cols-1][y].type = v;
			map.terrainBlocks[cols-1][y].type = VariableCollection.grass;
		}
		for (int x2 = 0; x2 < map.blocks.length; x2++) {
			for (int y2 = 0; y2 < map.blocks[0].length; y2++) {
				map.blocks[x2][y2].setSprite(x2,y2,map.blocks);
			}
		}

		if(wall) {
			if(bobX >= map.cols || bobY >= map.rows || bobX <= 0 || bobY <= 0) {
				bobX = 1; bobY = 1;
				map.blocks[bobX][bobY] = new Block(VariableCollection.nothing,bobX,bobY);
				System.out.println("Reset GreenHatMan location.");
			}
		}

		bob.setPosition(bobX, bobY);
	}

	public void setMapSize(int xsize, int ysize) {
		Block[][] oldBlocks = map.blocks;
		TerrainBlock[][] oldTerrains = map.terrainBlocks;

		int oldXsize = oldBlocks==null?0:oldBlocks.length, oldYsize = oldBlocks==null?0:oldBlocks[0].length;
		if (wallsAround) {
			this.wallEdges(false, oldYsize, oldXsize);
			System.out.println("Wallin' begone");
			wallsAround = true;
		}

		map.cols = xsize;
		map.rows = ysize;

		map.terrainBlocks = new TerrainBlock[map.cols][map.rows];
		map.blocks = new Block[map.cols][map.rows];
		map.renderAtRows = new Array<Array<Block>>();
		for (int i = 0; i < map.rows; i++) {
			map.renderAtRows.add(new Array<Block>());
		}

		for (int x = 0; x < map.terrainBlocks.length; x++) {
			for (int y = 1; y <= map.terrainBlocks[0].length; y++) {
				if(x >= oldXsize || y > oldYsize) {
					map.blocks[x][map.blocks[x].length-y] = 
						new Block(VariableCollection.nothing,x,map.blocks[x].length-y);
					map.terrainBlocks[x][map.terrainBlocks[x].length-y] = 
						new TerrainBlock(VariableCollection.grass,x,map.terrainBlocks[x].length-y);
				} else {
					map.blocks[x][map.blocks[x].length-y] = oldBlocks[x][oldBlocks[x].length-y];
					map.blocks[x][map.blocks[x].length-y].tileX = x;
					map.blocks[x][map.blocks[x].length-y].tileY = map.blocks[x].length-y;
					map.terrainBlocks[x][map.terrainBlocks[x].length-y] = oldTerrains[x][oldTerrains[x].length-y];
					map.terrainBlocks[x][map.terrainBlocks[x].length-y].tileX = x;
					map.terrainBlocks[x][map.terrainBlocks[x].length-y].tileY = map.terrainBlocks[x].length-y;
					map.terrainBlocks[x][map.terrainBlocks[x].length-y].spriteX = x*64f;
					map.terrainBlocks[x][map.terrainBlocks[x].length-y].spriteY = 
						(map.terrainBlocks[x].length-y)*64f;
				}
			}
		}

		if(bobX > xsize || bobY > ysize) {
			bobX = 0; bobY = 0;
			map.blocks[bobX][bobY] = new Block(VariableCollection.nothing,bobX,bobY);
			System.out.println("Reset GreenHatMan location.");
		}

		bob.setPosition(bobX, bobY);

		minZoomValue = 0.1f;
		maxZoomValue = 4f;

		if (wallsAround) {
			this.wallEdges(true, map.rows, map.cols);
			System.out.println("Wallin'");
		}

		for(int c = 0; c < map.cols; c++) {
			for(int r = 0; r < map.rows; r++) {
				map.renderAtRows.get(r).add(map.blocks[c][r]);
			}
		}
		rm.setCols(map.cols);
		rm.setRows(map.rows);
	}

	@Override
	public boolean touchMoved(int arg0, int arg1) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean touchUp(int x, int y, int pointer, int button) {
		draggedToColnr = Integer.MAX_VALUE;
		draggedToRownr = Integer.MAX_VALUE;
		pressedLMF = false;
		pressedCTB = false;
		pressedCBB = false;
		draggingRow = false;
		draggingCol = false;
		// for pinch-to-zoom           
		if(numberOfFingers == 1)
		{
			//Vector3 touchPoint = new Vector3(x, y, 0);
			//cam.unproject(touchPoint);
		}
		numberOfFingers--;


		// just some error prevention... clamping number of fingers (ouch! :-)
		if(numberOfFingers<0){
			numberOfFingers = 0;
		}


		lastDistance = 0;
		return false;
	}

	@Override
	public void update() {
		ctb.update();
		cbb.update();
	}

	@Override
	public void render() {
		Renderer.getInstance().renderMapEditor(this,map, bob);
	}

	@Override
	public void enter() {
		Gdx.input.setCatchBackKey(true);
		Gdx.input.setInputProcessor(this);
	}

	public String restartTest() {
		((Game) main.getState(Main.SCREEN_GAME)).testLevel(map.toString(bob));
		return null;
	}	
}